#include "hal.h"
#include "dbg_tool.h"
#include "perf.h"
#include "tpcan.h"
#include "bat_zhiann.h"
#include "utils_str.h"

/*-----------------------------------------------------------------------------
 * 适配器协议说明：
    * 1. 适配器通过CAN总线与智安BMS通信，使用TPCAN协议栈进行数据传输和接收。
    * 2. 适配器通过串口与上位机通信，接收控制命令并反馈BMS状态信息。
    * 3. 适配器通过GPIO控制充电器的电源开关，并读取充电开关状态。

(1) 开启充电器: $AT+Charger=ON
    *     适配器接收到该命令后，设置充电器电源引脚为高电平，启动充电器。
    ========== 电池开机(2块) + 打开充电器IO + 打开充电FET ==========
    检测双电池情况下，判断是否电压差<1v，若是则同时打开充电器，否则禁止充电，并通过串口反馈错误信息给上位机。

(2) 关闭充电器: $AT+Charger=OFF
    *     适配器接收到该命令后，设置充电器电源引脚为低电平，关闭充电器。
    ==================== 关闭充电器IO  ====================

(3) 电池开机: $AT+BMS=ON
    *     适配器接收到该命令后，向智安BMS发送开机指令。
    两块BMS均发送开机指令后

(4) 电池关机: $AT+BMS=OFF
    *     适配器接收到该命令后，向智安BMS发送关机指令。

-----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
 * macros define
-----------------------------------------------------------------------------*/
#define TPCAN_NODE_ID       0x81u // for Zhiann BMS
#define MAIN_CAN            HAL_CAN0
#define APP_MIN_RECV_LEN    8U
#define TIEMOUT_COUNT       3U
#define BMS1_ID             51
#define BMS2_ID             52
#define MAIN_COM            HAL_COM0

/*-----------------------------------------------------------------------------
 * local variates
-----------------------------------------------------------------------------*/
static tpcan_inst_t _tpcan;

#define CHARGER_PWR_ON()         hal_gpio_set(HAL_GPIO_PIN_1)
#define CHARGER_PWR_OFF()        hal_gpio_reset(HAL_GPIO_PIN_1)
#define CHARGER_SWITCH_IS_ON()  (1==hal_gpio_read(HAL_GPIO_PIN_4))
#define CHARGER_SWITCH_IS_OFF() (0==hal_gpio_read(HAL_GPIO_PIN_4))

enum ACTION_CODE
{
    ACTION_CODE_NONE = 0,
    ACTION_CODE_CHARGER_ON,
    ACTION_CODE_CHARGER_OFF,
    ACTION_CODE_BMS_ON,
    ACTION_CODE_BMS_OFF,
};
static enum ACTION_CODE _action_code = ACTION_CODE_NONE;

static zhiann_bat_t _zhn_bat[2];
static bool _is_volt_balance = false;
static bool _is_bms_ready = false;

/*-----------------------------------------------------------------------------
 * local functions
-----------------------------------------------------------------------------*/
/**
 * @brief Process TPCAN Request transfer packet
 */
static void tpcan_proc_req(tpcan_inst_t* ins, tpcan_rx_trans_t* trans)
{
    UNUSED(ins);
    switch (trans->dt_id)
    {
    default:;
    }
}

/**
 * @brief Process TPCAN Response transfer packet
 */
static void tpcan_proc_rsp(tpcan_inst_t* ins, tpcan_rx_trans_t* trans)
{
    UNUSED(ins);
    switch (trans->dt_id)
    {
    default:;
    }
}

/**
 * @brief Process TPCAN Broadcast transfer packet
 */
static void tpcan_proc_bcst(tpcan_inst_t* ins, tpcan_rx_trans_t* trans)
{
    const U8 data_len = trans->rx_item->blk->data[0];
    const U8* data_ptr = &trans->rx_item->blk->data[1];
    if(IS_ZHIANN_BAT_MSG_ID(trans->dt_id))
    {        
        if(trans->src_node_id == BMS1_ID)
        {
            zhiann_bat_analyze(&_zhn_bat[0], trans->dt_id, data_ptr, data_len);
        }
        else if(trans->src_node_id == BMS2_ID)
        {
            zhiann_bat_analyze(&_zhn_bat[1], trans->dt_id, data_ptr, data_len);
        }
    }//~ IS_ZHIANN_BAT_MSG_ID
}

/**
 * @brief Call back function for CAN frame is ready (call in ISR)
 */
static void on_can_frame_ready(const U8 bus_id, can_frame_t* frame)
{
    tpcan_handle_can_frame(&_tpcan, frame);
}

/**
 * @brief Check if cannode message can be accept
 */
static bool on_should_accept(
    tpcan_inst_t* ins,           /* Library instance               */
    U64* signature,              /* Data type signature            */
    const U16 dt_id,             /* Refer to the specification     */
    tpcan_trans_type trans_type, /* Refer to tpcan_trans_type      */
    const U8 src_node_id)        /* Source node ID or Broadcast(0) */
{
    assert(ins == &_tpcan);

    /* process broadcast frame */
    if (trans_type == TRANS_TYPE_BCST)
    {
        if (ZHIANN_BAT_ID1_ID == dt_id)
        {
            *signature = ZHIANN_BAT_ID1_SIG;
            return true;
        }
        if (ZHIANN_BAT_ID2_ID == dt_id)
        {
            *signature = ZHIANN_BAT_ID2_SIG;
            return true;
        }
        if (ZHIANN_BAT_INFO_ID == dt_id)
        {
            *signature = ZHIANN_BAT_INFO_SIG;
            return true;
        }
        if (ZHIANN_BAT_AUX_INFO_ID == dt_id)
        {
            *signature = ZHIANN_BAT_AUX_INFO_SIG;
            return true;
        }
    }

    if (trans_type == TRANS_TYPE_REQ)
    {
    }

    if (trans_type == TRANS_TYPE_RSP)
    {
    }
    return true;
}

/**
 * @brief Get Full-Frame in Transfer layer [Call In ISR]
 */
static void on_transfer_reception(tpcan_inst_t* ins, tpcan_rx_trans_t* trans)
{
    switch (trans->trans_type)
    {
    case TRANS_TYPE_REQ:
        tpcan_proc_req(&_tpcan, trans);
        break;
    case TRANS_TYPE_RSP:
        tpcan_proc_rsp(&_tpcan, trans);
        break;
    case TRANS_TYPE_BCST:
        tpcan_proc_bcst(&_tpcan, trans);
        break;
    default:;
    }
}

static void can_initialize(void)
{
    /* Init TPCAN protocol instance */
    tpcan_init(&_tpcan, TPCAN_NODE_ID, false);
    _tpcan.on_reception = on_transfer_reception;
    _tpcan.should_accept = on_should_accept;

    /* Init hal can driver */
    if (hal_can_init(MAIN_CAN))
    {
        hal_can_reg_rx_ready(MAIN_CAN, on_can_frame_ready);
        trace_info("[CAN%d]:Initial OK", MAIN_CAN);
    }
    else
    {
        trace_error("[CAN%d]:Initial error", MAIN_CAN);
    }
}

static U8 _trans_id;
static void zhiann_bms_power_off(void)
{
    zhiann_cmd_req_t req;
    req.cmd = ZHIANN_BAT_CMD_POWER_OFF;
    req.type = ZHIANN_BAT_REQ_TYPE_FLY_CMD;
    req.param1 = 0xA5;
    req.param2 = 0;

    tpcan_msg_t msg;
    msg.trans_id = &_trans_id;
    msg.dt_id = ZHIANN_BAT_CMD_ID;
    msg.len = sizeof(zhiann_cmd_req_t);
    msg.payload = (U8*)&req;
    msg.priority = TPCAN_TRANS_PRIO_LOW;

    tpcan_send_req(&_tpcan, BMS1_ID, &msg);
    tpcan_flush_tx_queue(&_tpcan, MAIN_CAN);

    hal_delay_ms(100);

    tpcan_send_req(&_tpcan, BMS2_ID, &msg);
    tpcan_flush_tx_queue(&_tpcan, MAIN_CAN);
}

static void zhiann_bms_power_on(void)
{
    zhiann_cmd_req_t req;
    req.cmd = ZHIANN_BAT_CMD_POWER_ON;
    req.type = ZHIANN_BAT_REQ_TYPE_FLY_CMD;
    req.param1 = 0xA5;
    req.param2 = 0;

    tpcan_msg_t msg;
    msg.trans_id = &_trans_id;
    msg.dt_id = ZHIANN_BAT_CMD_ID;
    msg.len = sizeof(zhiann_cmd_req_t);
    msg.payload = (U8*)&req;
    msg.priority = TPCAN_TRANS_PRIO_LOW;

    tpcan_send_req(&_tpcan, BMS1_ID, &msg);
    tpcan_flush_tx_queue(&_tpcan, MAIN_CAN);

    hal_delay_ms(100);

    tpcan_send_req(&_tpcan, BMS2_ID, &msg);
    tpcan_flush_tx_queue(&_tpcan, MAIN_CAN);
}

static void zhiann_bms_charge_start(void)
{
    zhiann_cmd_req_t req;
    req.cmd = ZHIANN_BAT_CMD_START_CHARGE;
    req.type = ZHIANN_BAT_REQ_TYPE_FLY_CMD;
    req.param1 = 0xA5;
    req.param2 = 0;

    tpcan_msg_t msg;
    msg.trans_id = &_trans_id;
    msg.dt_id = ZHIANN_BAT_CMD_ID;
    msg.len = sizeof(zhiann_cmd_req_t);
    msg.payload = (U8*)&req;
    msg.priority = TPCAN_TRANS_PRIO_LOW;

    tpcan_send_req(&_tpcan, BMS1_ID, &msg);
    tpcan_flush_tx_queue(&_tpcan, MAIN_CAN);

    hal_delay_ms(100);

    tpcan_send_req(&_tpcan, BMS2_ID, &msg);
    tpcan_flush_tx_queue(&_tpcan, MAIN_CAN);
}

static void zhiann_bms_charge_stop(void)
{
    zhiann_cmd_req_t req;
    req.cmd = ZHIANN_BAT_CMD_STOP_CHARGE;
    req.type = ZHIANN_BAT_REQ_TYPE_FLY_CMD;
    req.param1 = 0xA5;
    req.param2 = 0;

    tpcan_msg_t msg;
    msg.trans_id = &_trans_id;
    msg.dt_id = ZHIANN_BAT_CMD_ID;
    msg.len = sizeof(zhiann_cmd_req_t);
    msg.payload = (U8*)&req;
    msg.priority = TPCAN_TRANS_PRIO_LOW;

    tpcan_send_req(&_tpcan, BMS1_ID, &msg);
    tpcan_flush_tx_queue(&_tpcan, MAIN_CAN);

    hal_delay_ms(100);

    tpcan_send_req(&_tpcan, BMS2_ID, &msg);
    tpcan_flush_tx_queue(&_tpcan, MAIN_CAN);
}

static void on_com_rx_ready(const U8 port_num)
{
    for(;;)
    {
        U8 rx_buf[32];
        U32 len = hal_uart_read(port_num, rx_buf, sizeof(rx_buf));
        if(len == 0) break;
        // trace_buffer(rx_buf, len);

        const char* cmd = "$AT+Charger=ON";
        if(str_util_is_equal((const char*)rx_buf, cmd, strlen(cmd)))
        {
            trace_info("Charger ON");
            _action_code = ACTION_CODE_CHARGER_ON;
        }

        cmd = "$AT+Charger=OFF";
        if(str_util_is_equal((const char*)rx_buf, cmd, strlen(cmd)))
        {
            trace_info("Charger OFF");
            _action_code = ACTION_CODE_CHARGER_OFF;
        }

        cmd = "$AT+BMS=ON";
        if(str_util_is_equal((const char*)rx_buf, cmd, strlen(cmd)))
        {
            trace_info("BMS ON");
            _action_code = ACTION_CODE_BMS_ON;
        }

        cmd = "$AT+BMS=OFF";
        if(str_util_is_equal((const char*)rx_buf, cmd, strlen(cmd)))
        {
            trace_info("BMS OFF");
            _action_code = ACTION_CODE_BMS_OFF;
        }
    }
}

static void com_initialize(void)
{
    /* default 115200,8,n,1 */
    const uart_cfg_t cfg =
    {
        .baudrate = HAL_BAUDRATE_115200,
        .databit = HAL_UART_DATABIT_8,
        .stopbit = HAL_UART_STOPBIT_1,
        .parity = HAL_UART_PARITY_NONE
    };
    hal_uart_init(MAIN_COM, &cfg);
    hal_uart_reg_rx_ready(MAIN_COM, on_com_rx_ready);
}

static void report_bms_status(void)
{
    static U32 ts_last = 0;
    U32 ts_now = hal_hrt_get_ms();
    
    if(ts_now - ts_last > 1000u)
    {
        // Send BMS status to PC

        ts_last = ts_now;
    }
}
/**
 *@brief main function
 */
int main(void)
{
    /* Initiate the HAL libraries */
    hal_init();

    can_initialize();

    com_initialize();
  
    // Send idle frame to active BMS
    for(unsigned i=0; i<3; i++)
    {
        hal_delay_ms(1000);
        hal_can_transmit(MAIN_CAN, 255, NULL, 8);        
    }

    _is_bms_ready = _zhn_bat[0].ts != 0 || _zhn_bat[1].ts != 0;

    // Check if exist BMS status
    if(_is_bms_ready)
    {
        if(_zhn_bat[0].ts != 0 && _zhn_bat[1].ts != 0) 
        {               
            _is_volt_balance = fabsf(_zhn_bat[0].volt - _zhn_bat[1].volt) < 2.0f;
        }
        else
        {
            _is_volt_balance = true;
        }
    }

    /* Initial BMS IC device    */
    while (1)
    {
        /* toggle LED1 */
        hal_led_toggle(HAL_LED0);
        hal_delay_ms(20);

        if(ACTION_CODE_CHARGER_ON == _action_code)
        {
            if(_is_volt_balance)
            {
                CHARGER_PWR_ON();           
                zhiann_bms_charge_start();
            }         
        }
        
        if(ACTION_CODE_CHARGER_OFF == _action_code)
        {
            CHARGER_PWR_OFF();
        }
        
        if(ACTION_CODE_BMS_ON == _action_code)
        {
            zhiann_bms_power_on();
        }
        
        if(ACTION_CODE_BMS_OFF == _action_code)
        {
            zhiann_bms_power_off();
        }

        _action_code = ACTION_CODE_NONE;
        
        report_bms_status();
    }
}
